/*
Portions Copyright 2019-2021 ZomboDB, LLC.
Portions Copyright 2021-2022 Technology Concepts & Design, Inc. <support@tcdi.com>

All rights reserved.

Use of this source code is governed by the MIT license that can be found in the LICENSE file.
*/

use ogx::prelude::*;
use ogx::Array;
use serde::*;

ogx::pg_module_magic!();

#[og_extern]
fn sq_euclid_ogx(a: Array<f32>, b: Array<f32>) -> f32 {
    a.iter_deny_null().zip(b.iter_deny_null()).map(|(a, b)| (a - b) * (a - b)).sum()
}

#[og_extern(immutable, parallel_safe)]
fn approx_distance_ogx(compressed: Array<i64>, distances: Array<f64>) -> f64 {
    compressed
        .iter_deny_null()
        .map(|cc| {
            let d = distances.get(cc as usize).unwrap().unwrap();
            ogx::info!("cc={}, d={}", cc, d);
            d
        })
        .sum()
}

#[og_extern]
fn default_array() -> Vec<i32> {
    Default::default()
}

#[og_extern(requires = [ default_array, ])]
fn sum_array(input: default!(Array<i32>, "default_array()")) -> i64 {
    let mut sum = 0 as i64;

    for i in input {
        sum += i.unwrap_or(-1) as i64;
    }

    sum
}

#[og_extern]
fn sum_vec(mut input: Vec<Option<i32>>) -> i64 {
    let mut sum = 0 as i64;

    input.push(Some(6));

    for i in input {
        sum += i.unwrap_or_default() as i64;
    }

    sum
}

#[og_extern]
fn static_names() -> Vec<Option<&'static str>> {
    vec![Some("Brandy"), Some("Sally"), None, Some("Anchovy")]
}

#[og_extern]
fn static_names_set() -> SetOfIterator<'static, Vec<Option<&'static str>>> {
    SetOfIterator::new(
        vec![
            vec![Some("Brandy"), Some("Sally"), None, Some("Anchovy")],
            vec![Some("Eric"), Some("David")],
            vec![Some("ZomboDB"), Some("PostgreSQL"), Some("Elasticsearch")],
        ]
        .into_iter(),
    )
}

#[og_extern]
fn i32_array_no_nulls() -> Vec<i32> {
    vec![1, 2, 3, 4, 5]
}

#[og_extern]
fn i32_array_with_nulls() -> Vec<Option<i32>> {
    vec![Some(1), None, Some(2), Some(3), None, Some(4), Some(5)]
}

#[og_extern]
fn strip_nulls(input: Vec<Option<i32>>) -> Vec<i32> {
    input.into_iter().filter(|i| i.is_some()).map(|i| i.unwrap()).collect()
}

#[derive(OgType, Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct SomeStruct {}

#[og_extern]
#[search_path(@extschema@)]
fn return_vec_of_customtype() -> Vec<SomeStruct> {
    vec![SomeStruct {}]
}

#[cfg(test)]
pub mod og_test {

    pub fn setup(_options: Vec<&str>) {
        // perform one-off initialization when the og_test framework starts
    }

    pub fn opengauss_conf_options() -> Vec<&'static str> {
        // return any postgresql.conf settings that are required for your tests
        vec![]
    }
}

#[og_schema]
#[cfg(any(test, feature = "og_test"))]
pub mod tests {
    use crate::SomeStruct;
    use ogx::prelude::*;

    #[og_test]
    #[search_path(@extschema@)]
    fn test_vec_of_customtype() {
        let customvec =
            Spi::get_one::<Vec<SomeStruct>>("SELECT arrays.return_vec_of_customtype();")
                .expect("SQL select failed");
        assert_eq!(customvec, vec![SomeStruct {}]);
    }
}
